package com.yami.shop.service.impl;


import ch.qos.logback.core.joran.spi.XMLUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConstants;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.paypal.orders.*;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.yami.shop.bean.app.param.PayInfoParam;
import com.yami.shop.bean.bo.PayInfoBo;
import com.yami.shop.bean.bo.RefundInfoBo;
import com.yami.shop.bean.enums.BackType;
import com.yami.shop.bean.event.BalancePayEvent;
import com.yami.shop.bean.event.BalanceRefundEvent;
import com.yami.shop.bean.model.OrderSettlement;
import com.yami.shop.bean.param.PayPalOrderParam;
import com.yami.shop.bean.param.WebhookParam;
import com.yami.shop.bean.pay.PayInfoDto;
import com.yami.shop.bean.pay.RefundInfoDto;
import com.yami.shop.common.bean.Domain;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.enums.PayType;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.Arith;
import com.yami.shop.common.util.IPHelper;
import com.yami.shop.common.wx.HttpClientUtils;
import com.yami.shop.common.wx.PayCommonUtil;
import com.yami.shop.common.wx.SignUtil;
import com.yami.shop.config.AliPayConfig;
import com.yami.shop.config.PayPalConfig;
import com.yami.shop.config.ShopConfig;
import com.yami.shop.config.WxConfig;
import com.yami.shop.service.OrderSettlementService;
import com.yami.shop.service.PayManagerService;
import lombok.AllArgsConstructor;
import okhttp3.HttpUrl;
import org.apache.http.client.methods.HttpPost;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ma.glasnost.orika.MapperFacade;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static com.github.binarywang.wxpay.constant.WxPayConstants.SignType.MD5;

/**
 * 统一支付工具
 *
 * @author LGH
 */
@Service
@AllArgsConstructor
public class PayManagerServiceImpl implements PayManagerService {

    private AliPayConfig aliPayConfig;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private ShopConfig shopConfig;

    private WxConfig wxConfig;

    private PayPalConfig payPalConfig;

    private final ApplicationContext applicationContext;

    private final MapperFacade mapperFacade;

    private final OrderSettlementService orderSettlementService;

    private static final String WX_SUCCESS_XML = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
    private static final String ALI_SUCCESS = "success";

    @Override
    public ResponseEntity<?> doPay(HttpServletResponse httpResponse, PayInfoDto payInfo) throws WxPayException, IOException, AlipayApiException {

        if (payInfo.getPayAmount() < 0.01 && payInfo.getIsScore() == 1) {
            return ResponseEntity.ok().build();
        }

        //微信支付不能大于1000w
        if (payInfo.getPayAmount() < 0.01 || payInfo.getPayAmount() > 10000000.00) {
            // 订单金额有误，无法进行支付
            throw new YamiShopBindException("yami.order.amount.error");
        }
        String domainName = shopConfig.getDomain().getApiDomainName();
        // 如果是商家端需要回调，则使用商家端的地址
        if (Objects.nonNull(payInfo.getBackType()) && Objects.equals(payInfo.getBackType(), BackType.SHOP.value())) {
            domainName = shopConfig.getDomain().getMultishopDomainName();
        }
        String notifyUrl = domainName + payInfo.getApiNoticeUrl();
        //String notifyUrl = "http://3k9cn7.natappfree.cc" + payInfo.getApiNoticeUrl();
        PayType payType = PayType.instance(payInfo.getPayType());
        if (isWxPay(payType)) {


            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            orderRequest.setBody(payInfo.getBody());
            orderRequest.setOutTradeNo(payInfo.getPayNo());
            orderRequest.setTotalFee((int) Arith.mul(payInfo.getPayAmount(), 100));
            orderRequest.setSpbillCreateIp(IPHelper.getIpAddr());
            orderRequest.setOpenid(payInfo.getBizUserId());

            orderRequest.setNotifyUrl(notifyUrl);

            // 微信小程序支付
            if (Objects.equals(payType, PayType.WECHATPAY)) {
//                WxPayService wxPayService = wxConfig.getWxPayService(payType);
//                orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
//                WxPayMpOrderResult wxPayMpOrderResult = wxPayService.createOrder(orderRequest);
//                return ResponseEntity.ok(wxPayMpOrderResult);
                System.out.println("====================进入微信小程序支付《《《《《《《《《《《《《《《《《《《《《《《《《《《《《");
                orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
                Properties properties = PropertiesLoaderUtils.loadAllProperties("config/wechatPay.properties");
                String mchid = properties.getProperty("wechat.mchIdJsapi");
                String appId = properties.getProperty("wechat.appIdJsapi");
                JSONObject paramMap = new JSONObject();
                JSONObject object = new JSONObject();
                paramMap.put("mchid", mchid);
                paramMap.put("out_trade_no", payInfo.getPayNo());
                paramMap.put("appid", appId);
                paramMap.put("description", "沙鲜送-" + orderRequest.getBody());
                paramMap.put("notify_url", properties.getProperty("wechat.wxNotifyUrl2"));

                object.put("total", orderRequest.getTotalFee());
                object.put("currency", "CNY");       //货币类型
                paramMap.put("amount", object);     //金额及货币类型

                object = new JSONObject();
                object.put("openid", payInfo.getBizUserId());
                paramMap.put("payer", object);

                object = new JSONObject();
                object.put("payer_client_ip",payInfo.getIp());
                paramMap.put("scene_info",object);
                HttpUrl url = HttpUrl.parse(properties.getProperty("wechat.wxPayOrderJsapi"));
                long timestamp = System.currentTimeMillis() / 1000;            //时间戳
                String nonceStr = SignUtil.generateNonceStr();                 //随机字符串
                String body = JSONObject.toJSONString(paramMap);                //参数
                String serial_no = properties.getProperty("wechat.secretJsapi");//    //证书序列号
                String message = SignUtil.buildMessage("POST", url, timestamp, nonceStr, body);
                //签名值
                String signVal = SignUtil.sign(message.getBytes("utf-8"));
                System.out.println("调用支付前的订单编号=====================================================>>>>paramMap===" + paramMap);
                String auth = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + mchid + "\",nonce_str=\"" + nonceStr + "\",signature=\"" + signVal + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + serial_no + "\"";
                System.out.println(JSONObject.toJSONString(paramMap) + "____________________________________________");
                String str = HttpClientUtils.httpPost(JSONObject.toJSONString(paramMap), properties.getProperty("wechat.wxPayOrderJsapi"), auth);
                JSONObject obj = JSONObject.parseObject(str);
                String prepayid = obj.getString("prepay_id");
                obj = new JSONObject();
                obj.put("appid", appId);
                obj.put("timestamp", Long.toString(timestamp));
                obj.put("noncestr", nonceStr);
                obj.put("package", "prepay_id=" + prepayid);
                obj.put("signtype", "RSA");
                message = SignUtil.buildMessage2(appId, timestamp, nonceStr, "prepay_id=" + prepayid);
//签名值
                signVal = SignUtil.sign(message.getBytes("utf-8"));
                obj.put("paysign", signVal);
                obj.put("appid",appId);
                obj.put("timestamp",Long.toString(timestamp));
                obj.put("noncestr",nonceStr);
                obj.put("package","prepay_id=" + prepayid);
                obj.put("signtype","RSA");
                message = SignUtil.buildMessage2( appId, timestamp, nonceStr, "prepay_id=" + prepayid);
                //签名值
                signVal = SignUtil.sign(message.getBytes("utf-8"));
                obj.put("paysign",signVal);
                System.out.println("====================结束微信小程序支付《《《《《《《《《《《《《《《《《《《《《《《《《《《《《");
                System.out.println(JSONObject.toJSONString(obj));
                return ResponseEntity.ok(JSONObject.toJSONString(obj));
            }
            // 微信网页支付
            else if (Objects.equals(payType, PayType.WECHATPAY_SWEEP_CODE)) {
                WxPayService wxPayService = wxConfig.getWxPayService(payType);
                orderRequest.setProductId(payInfo.getPayNo());
                // 生成微信二维码
                orderRequest.setTradeType(WxPayConstants.TradeType.NATIVE);
                Properties properties = PropertiesLoaderUtils.loadAllProperties("config/wechatPay.properties");
                String mchid = properties.getProperty("wechat.mchId");
                String appId = properties.getProperty("wechat.appId");

                Map<String, Object> paramMap = new HashMap<String, Object>();
                Map<String, Object> object = new HashMap<String, Object>();
                paramMap.put("appid", appId);
                paramMap.put("mchid", mchid);
                paramMap.put("description", "沙鲜送商城-" + orderRequest.getBody());
                paramMap.put("out_trade_no", payInfo.getPayNo());
//                paramMap.put("time_expire", 30*60*1000);

                object.put("total", orderRequest.getTotalFee());
                object.put("currency", "CNY");       //货币类型
                paramMap.put("amount", object);     //金额及货币类型

                object = new JSONObject();
                // object.put("payer_client_ip",payInfo.getIp());
                object.put("payer_client_ip", "192.168.31.189");

                paramMap.put("scene_info", object);

                paramMap.put("notify_url", properties.getProperty("wechat.wxNotifyUrl1"));


                HttpUrl url = HttpUrl.parse(properties.getProperty("wechat.wxPayOrderNative"));
                long timestamp = System.currentTimeMillis() / 1000;            //时间戳
                String nonceStr = SignUtil.generateNonceStr();                 //随机字符串
                String body = JSONObject.toJSONString(paramMap);                //参数
                String serial_no = properties.getProperty("wechat.secret");//    //证书序列号

                String message = SignUtil.buildMessage("POST", url, timestamp, nonceStr, body);
                //签名值
                String signVal = SignUtil.sign(message.getBytes("utf-8"));
                // System.out.println("调用支付前的订单编号=====================================================>>>>paramMap==="+paramMap);
                String auth = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + mchid + "\",nonce_str=\"" + nonceStr + "\",signature=\"" + signVal + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + serial_no + "\"";
                String str = HttpClientUtils.httpPost(JSONObject.toJSONString(paramMap), properties.getProperty("wechat.wxPayOrderNative"), auth);
                //JSONObject obj = JSONObject.parseObject(str);
                //WxPayMwebOrderResult wxPayMwebOrderResult = wxPayService.createOrder(orderRequest);
                return ResponseEntity.ok(str);
            }
            // 微信H5支付
            else if (Objects.equals(payType, PayType.WECHATPAY_H5)) {
                // WxPayService wxPayService = wxConfig.getWxPayService(payType);
                // 生成微信二维码
                orderRequest.setTradeType(WxPayConstants.TradeType.MWEB);
//                String s = "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"\",\"wap_name\": \"\"}}";
//                orderRequest.setSceneInfo(s);

                Properties properties = PropertiesLoaderUtils.loadAllProperties("config/wechatPay.properties");
                String mchid = properties.getProperty("wechat.mchId");
                String appId = properties.getProperty("wechat.appId");
                String time = "";
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+08:00");
                Calendar afterTime = Calendar.getInstance();
                //  if(3 == payInfo.getOrderType()){
//                    afterTime.add(Calendar.SECOND, payInfo.getCountDownSecond());// 秒杀订单超时时间
//                    afterTime.add(Calendar.MINUTE, payInfo.getCountDownMin());// 秒杀订单超时时间

//                Date beforeD = afterTime.getTime();
//                time = df.format(beforeD);  //订单失效时间
                Map<String, Object> paramMap = new HashMap<String, Object>();
                Map<String, Object> object = new HashMap<String, Object>();
                paramMap.put("appid", appId);
                paramMap.put("mchid", mchid);
                paramMap.put("description", "沙鲜送商城-" + orderRequest.getBody());
                paramMap.put("out_trade_no", payInfo.getPayNo());
//                paramMap.put("time_expire", 30*60);

                object.put("total", orderRequest.getTotalFee());
                object.put("currency", "CNY");       //货币类型
                paramMap.put("amount", object);     //金额及货币类型
                object = new JSONObject();
                object.put("payer_client_ip", payInfo.getIp());
                JSONObject object2 = new JSONObject();
                object2.put("type", "");
                object.put("h5_info", object2);      //场景信息
                paramMap.put("scene_info", object);
                paramMap.put("notify_url", properties.getProperty("wechat.wxNotifyUrl"));

                Map<String, String> headers = new HashMap<>();
                headers.put("Content-Type", "application/json;charset=utf-8");
                HttpUrl url = HttpUrl.parse(properties.getProperty("wechat.wxPayUnifiedOrder"));
                long timestamp = System.currentTimeMillis() / 1000;            //时间戳
                String nonceStr = SignUtil.generateNonceStr();                 //随机字符串
                String body = JSONObject.toJSONString(paramMap);                //参数
                String serial_no = properties.getProperty("wechat.secret");//    //证书序列号

                String message = SignUtil.buildMessage("POST", url, timestamp, nonceStr, body);
                //签名值
                String signVal = SignUtil.sign(message.getBytes("utf-8"));
//                System.out.println("调用支付前的订单编号=====================================================>>>>paramMap==="+paramMap);
                String auth = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + mchid + "\",nonce_str=\"" + nonceStr + "\",signature=\"" + signVal + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + serial_no + "\"";
                String str = HttpClientUtils.httpPost(JSONObject.toJSONString(paramMap), properties.getProperty("wechat.wxPayUnifiedOrder"), auth);
                //   WxPayMwebOrderResult wxPayMwebOrderResult = wxPayService.createOrder(orderRequest);
                return ResponseEntity.ok(str);
            }
            // 微信公众号支付
            else if (Objects.equals(payType, PayType.WECHATPAY_MP)) {
                WxPayService wxPayService = wxConfig.getWxPayService(payType);
                orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
                WxPayMpOrderResult wxPayMpOrderResult = wxPayService.createOrder(orderRequest);
                return ResponseEntity.ok(wxPayMpOrderResult);
            }
            // 微信app支付
            else if (Objects.equals(payType, PayType.WECHATPAY_APP)) {
                System.out.println("====================进入微信app支付《《《《《《《《《《《《《《《《《《《《《《《《《《《《《");
                orderRequest.setTradeType(WxPayConstants.TradeType.APP);
                Properties properties = PropertiesLoaderUtils.loadAllProperties("config/wechatPay.properties");
                String mchid = properties.getProperty("wechat.mchId");
                String appId = properties.getProperty("wechat.appId");
                Map<String, Object> paramMap = new HashMap<String, Object>();
                Map<String, Object> object = new HashMap<String, Object>();
                paramMap.put("appid", appId);
                paramMap.put("mchid", mchid);
                paramMap.put("description", "沙鲜送商城-" + orderRequest.getBody());
                paramMap.put("out_trade_no", payInfo.getPayNo());
                object.put("total", orderRequest.getTotalFee());
                object.put("currency", "CNY");       //货币类型
                paramMap.put("amount", object);     //金额及货币类型
                object = new JSONObject();
                object.put("payer_client_ip", payInfo.getIp());

                paramMap.put("scene_info", object);
                paramMap.put("notify_url", properties.getProperty("wechat.wxNotifyUrl"));
                HttpUrl url = HttpUrl.parse(properties.getProperty("wechat.wxPayUnifiedOrder"));
                long timestamp = System.currentTimeMillis() / 1000;            //时间戳
                String nonceStr = SignUtil.generateNonceStr();                 //随机字符串
                String body = JSONObject.toJSONString(paramMap);                //参数
                String serial_no = properties.getProperty("wechat.secret");//    //证书序列号
                String message = SignUtil.buildMessage("POST", url, timestamp, nonceStr, body);
                //签名值
                String signVal = SignUtil.sign(message.getBytes("utf-8"));
                System.out.println("调用支付前的订单编号=====================================================>>>>paramMap===" + paramMap);
                String auth = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + mchid + "\",nonce_str=\"" + nonceStr + "\",signature=\"" + signVal + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + serial_no + "\"";
                String str = HttpClientUtils.httpPost(JSONObject.toJSONString(paramMap), properties.getProperty("wechat.wxPayUnifiedOrder"), auth);
                JSONObject obj = JSONObject.parseObject(str);
                String prepayid = obj.getString("prepay_id");
                obj = new JSONObject();
                obj.put("appid", appId);
                obj.put("timestamp", Long.toString(timestamp));
                obj.put("noncestr", nonceStr);
                obj.put("prepayid", prepayid);

                String str2 = SignUtil.buildMessage2(appId, timestamp, nonceStr, prepayid);
                signVal = SignUtil.sign(str2.getBytes("utf-8"));
                obj.put("sign", signVal);
                obj.put("partnerid", mchid);
                obj.put("package", "Sign=WXPay");
                System.out.println("====================结束微信app支付《《《《《《《《《《《《《《《《《《《《《《《《《《《《《");
                System.out.println(JSONObject.toJSONString(obj));
                return ResponseEntity.ok(JSONObject.toJSONString(obj));
            }
        } else if (Objects.equals(payType, PayType.ALIPAY)) {
            //创建API对应的request
            //AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();

            //在公共参数中设置回跳和通知地址
//            alipayRequest.setNotifyUrl(notifyUrl);
//            alipayRequest.setReturnUrl(payInfo.getReturnUrl());
//            AlipayTradePagePayModel alipayTradePagePayModel = new AlipayTradePagePayModel();
//            alipayTradePagePayModel.setOutTradeNo(payInfo.getPayNo());
//            alipayTradePagePayModel.setBody(payInfo.getBody());
//            alipayTradePagePayModel.setSubject(payInfo.getBody());
//            alipayTradePagePayModel.setTotalAmount(Double.toString(payInfo.getPayAmount()));
//            alipayTradePagePayModel.setProductCode("FAST_INSTANT_TRADE_PAY");
//            alipayRequest.setBizModel(alipayTradePagePayModel);
            Properties properties = PropertiesLoaderUtils.loadAllProperties("config/alipayAuth.properties");
            String aliPublicKey = properties.getProperty("alipay.aliPublicKey");
            String privateKey = properties.getProperty("alipay.privateKey");
            String aliPayUrl = properties.getProperty("alipay.aliPayNative");
            String appId = properties.getProperty("alipay.appId");
            String signType = properties.getProperty("alipay.signType");
            //实例化客户端alipay.trade.wap.pay
            AlipayClient alipayClient = new DefaultAlipayClient(aliPayUrl, appId, privateKey, "json", "UTF-8", aliPublicKey, signType);
            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：
            //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
            AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
            model.setBody(payInfo.getBody());
            model.setSubject("沙鲜送商城-" + payInfo.getBody());
            model.setOutTradeNo(payInfo.getPayNo());
            model.setTotalAmount(Double.toString(payInfo.getPayAmount()));
            model.setProductCode("FAST_INSTANT_TRADE_PAY");
            //model.setQuitUrl(properties.getProperty("alipay.quitUrl"));
            request.setBizModel(model);
            request.setReturnUrl(properties.getProperty("alipay.returnUrlPc"));
            request.setNotifyUrl(properties.getProperty("alipay.aliPayBackUrlPc"));
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            return ResponseEntity.ok(response.getBody());

        } else if (Objects.equals(payType, PayType.ALIPAY_H5)) {

            Properties properties = PropertiesLoaderUtils.loadAllProperties("config/alipayAuth.properties");
            String aliPublicKey = properties.getProperty("alipay.aliPublicKey");
            String privateKey = properties.getProperty("alipay.privateKey");
            String aliPayUrl = properties.getProperty("alipay.aliPayUrl");
            String appId = properties.getProperty("alipay.appId");
            String signType = properties.getProperty("alipay.signType");
            //实例化客户端alipay.trade.wap.pay
            AlipayClient alipayClient = new DefaultAlipayClient(aliPayUrl, appId, privateKey, "json", "UTF-8", aliPublicKey, signType);
            AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：
            //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
            AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
            model.setBody(payInfo.getBody());
            model.setSubject("东惠商城-" + payInfo.getBody());
            model.setOutTradeNo(payInfo.getPayNo());
            model.setTotalAmount(Double.toString(payInfo.getPayAmount()));
            model.setProductCode(properties.getProperty("alipay.payType"));
            model.setQuitUrl(properties.getProperty("alipay.quitUrl"));
            request.setBizModel(model);
            request.setReturnUrl(properties.getProperty("alipay.returnUrl"));
            request.setNotifyUrl(properties.getProperty("alipay.aliPayBackUrl"));
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeWapPayResponse response = alipayClient.pageExecute(request, "get");
            return ResponseEntity.ok(response.getBody());
        }
        // 支付宝app支付
        else if (Objects.equals(payType, PayType.ALIPAY_APP)) {
            Properties properties = PropertiesLoaderUtils.loadAllProperties("config/alipayAuth.properties");
            String aliPublicKey = properties.getProperty("alipay.aliPublicKey");
            String privateKey = properties.getProperty("alipay.privateKey");
            String aliPayUrl = properties.getProperty("alipay.aliPayUrl");
            String appId = properties.getProperty("alipay.appId");
            String signType = properties.getProperty("alipay.signType");

            //实例化客户端alipay.trade.wap.pay
            AlipayClient alipayClient = new DefaultAlipayClient(aliPayUrl, appId, privateKey, "json", "UTF-8", aliPublicKey, signType);
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", payInfo.getPayNo());
            bizContent.put("total_amount", Double.toString(payInfo.getPayAmount()));
            bizContent.put("subject", "沙鲜送商城-" + payInfo.getBody());
            bizContent.put("product_code", signType);
            request.setBizContent(bizContent.toString());
            request.setReturnUrl(properties.getProperty("alipay.returnUrl"));
            request.setNotifyUrl(properties.getProperty("alipay.aliPayBackUrl"));

            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

            return ResponseEntity.ok(response.getBody());
        }
        //余额支付
        else if (Objects.equals(payType, PayType.BALANCE)) {
            PayInfoParam payInfoParam = new PayInfoParam();
            applicationContext.publishEvent(new BalancePayEvent(payInfo, payInfoParam));
            return ResponseEntity.ok(payInfoParam);
        }
        //PayPal支付
        else if (Objects.equals(payType, PayType.PAYPAL)) {
            payInfo.setApiNoticeUrl(notifyUrl);
            OrdersCreateRequest request = payPalConfig.createPayPalOrder(payInfo);
            PayPalOrderParam param = payPalConfig.getExcetuteHref(request);
            // 根据支付单号，更新第三方订单号
            orderSettlementService.updateBizOrderIdByPayno(payInfo.getPayNo(), param.getOrderId());
            // 创建钩子
            payPalConfig.createWebhook(payInfo.getApiNoticeUrl());
            return ResponseEntity.ok(param.getHref());
        }
        return null;
    }

    @Override
    public PayInfoBo validateAndGetPayInfo(HttpServletRequest request, PayType payType, String xmlData) throws UnsupportedEncodingException, WxPayException, AlipayApiException {

        PayInfoBo payInfoBo = new PayInfoBo();
        // 小程序支付
        if (isWxPay(payType)) {

            try {
                if (payType.toString().equals("WECHATPAY_APP") || payType.toString().equals("WECHATPAY_SWEEP_CODE") || payType.toString().equals("WECHATPAY")) {
                    Properties properties = PropertiesLoaderUtils.loadAllProperties("config/wechatPay.properties");
                    JSONObject object = JSONObject.parseObject(xmlData);
                    String returnCode = object.getString("event_type");
                    if ("TRANSACTION.SUCCESS".equalsIgnoreCase(returnCode)) {

                        String resource = object.getString("resource");
                        JSONObject resourceObj = JSONObject.parseObject(resource);
                        String ciphertext = resourceObj.getString("ciphertext");
                        String associatedData = resourceObj.getString("associated_data");
                        String nonce = resourceObj.getString("nonce");
                        String result = new AesUtil(properties.getProperty("wechat.APIv3").getBytes()).decryptToString(associatedData.getBytes(), nonce.getBytes(), ciphertext);

                        JSONObject resultObject = JSONObject.parseObject(result);
                        payInfoBo.setPayNo(resultObject.getString("out_trade_no"));
                        payInfoBo.setBizPayNo(resultObject.getString("transaction_id"));
                        payInfoBo.setIsPaySuccess(true);
                        payInfoBo.setSuccessString(WX_SUCCESS_XML);
                    } else {
                        payInfoBo.setIsPaySuccess(false);
                    }
                } else {
                    WxPayService wxPayService = wxConfig.getWxPayService(payType);
                    WxPayOrderNotifyResult parseOrderNotifyResult = wxPayService.parseOrderNotifyResult(xmlData);
                    payInfoBo.setPayNo(parseOrderNotifyResult.getOutTradeNo());
                    payInfoBo.setBizPayNo(parseOrderNotifyResult.getTransactionId());
                    payInfoBo.setIsPaySuccess(true);
                    payInfoBo.setSuccessString(WX_SUCCESS_XML);
                }
            } catch (Exception e) {
                logger.error(e.getMessage());
            }


        } else if (isAliPay(payType)) {
            //获取支付宝POST过来反馈信息
            Map<String, String> params = parseAliNotifyAndGetResult(request);

            if (Objects.equals(params.get("trade_status"), "TRADE_SUCCESS")) {
                //商户订单号,之前生成的带用户ID的订单号
                payInfoBo.setPayNo(params.get("out_trade_no"));
                //支付宝交易号
                payInfoBo.setBizPayNo(params.get("trade_no"));
                payInfoBo.setIsPaySuccess(true);
                payInfoBo.setSuccessString(ALI_SUCCESS);
            } else if (Objects.equals(params.get("trade_status"), "TRADE_CLOSED")) {
                //商户订单号,之前生成的带用户ID的订单号
                payInfoBo.setPayNo(params.get("out_trade_no"));
                //支付宝交易号
                payInfoBo.setBizPayNo(params.get("trade_no"));
                payInfoBo.setIsPaySuccess(false);
                payInfoBo.setSuccessString(ALI_SUCCESS);
            } else {
                payInfoBo.setIsPaySuccess(false);
            }

        } else if (Objects.equals(payType, PayType.PAYPAL)) {
            // webhook 回调
            Map<String, String> headers = payPalConfig.getHeadersInfo(request);
            WebhookParam webhookParam = JSON.parseObject(xmlData, WebhookParam.class);
            boolean isVerify = payPalConfig.verifyWebhookSign(request, headers, xmlData);
            if (!isVerify) {
                // 验签失败
                throw new YamiShopBindException("yami.sign.check");
            }
            String orderId = webhookParam.getResource().getId();
            if (StrUtil.isNotBlank(orderId)) {
                System.out.println("token 回调： " + orderId);
                // 查询是否支付完成如果系统订单是已经支付完成就退款
                // 通过biz_order_no  = orderId 查询到支付记录
                OrderSettlement settlement = orderSettlementService.getOne(new LambdaQueryWrapper<OrderSettlement>()
                        .eq(OrderSettlement::getBizOrderNo, orderId).last("limit 1"));
                if (Objects.isNull(settlement)) {
                    // 找不到支付记录
                    throw new YamiShopBindException("找不到支付记录");
                }
                if (Objects.equals(1, settlement.getPayStatus())) {
                    // 已支付完成,支付失败，订单不在未支付状态
                    throw new YamiShopBindException("yami.order.pay.fail");
                }
                // 执行扣款操作
                payInfoBo = paypalCapture(orderId);
            } else {
                payInfoBo.setIsPaySuccess(false);
            }
        }
        return payInfoBo;
    }

    @Override
    public PayInfoBo getPayInfo(PayType payType, String payNo) {
        PayInfoBo payInfo = new PayInfoBo();
        payInfo.setPayNo(payNo);

        try {
            if (isWxPay(payType)) {
                WxPayService wxPayService = wxConfig.getWxPayService(payType);
                WxPayOrderQueryResult wxPayOrderQueryResult = wxPayService.queryOrder(null, payNo);
                if (!Objects.isNull(wxPayOrderQueryResult) && Objects.equals(wxPayOrderQueryResult.getTradeState(), Constant.SUCCESS)) {
                    payInfo.setBizPayNo(wxPayOrderQueryResult.getTransactionId());
                    payInfo.setIsPaySuccess(true);
                } else {
                    payInfo.setIsPaySuccess(false);
                }
            } else if (isAliPay(payType)) {
                AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
                AlipayTradeQueryModel alipayTradeQueryModel = new AlipayTradeQueryModel();
                alipayTradeQueryModel.setOutTradeNo(payNo);

                request.setBizModel(alipayTradeQueryModel);
                AlipayTradeQueryResponse response = aliPayConfig.getAlipayClient().execute(request);
                if (response.isSuccess()) {
                    payInfo.setBizPayNo(response.getTradeNo());
                    payInfo.setIsPaySuccess(true);
                } else {
                    payInfo.setIsPaySuccess(false);
                }
            } else {
                payInfo.setIsPaySuccess(false);
            }
        } catch (WxPayException | AlipayApiException e) {
            e.printStackTrace();
            payInfo.setIsPaySuccess(false);
        }
        return payInfo;
    }

    @Override
    public boolean doRefund(RefundInfoDto refundInfo) {

        PayType payType = refundInfo.getPayType();
        if (refundInfo.getRefundAmount() == 0) {
            return true;
        }
//        Domain domain = shopConfig.getDomain();

        // 提交提款操作
        try {
            Properties properties = PropertiesLoaderUtils.loadAllProperties("config/wechatPay.properties");
            if (isWxPay(payType)) {
                // 退款操作
                // 生成退款请求对象
                WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
                // 商户订单号
                wxPayRefundRequest.setOutTradeNo(refundInfo.getPayNo());
                // 订单金额
                double totalFee = Arith.mul(refundInfo.getPayAmount(), 100);
                wxPayRefundRequest.setTotalFee((int) totalFee);
                // 退款金额
                double refundFee = Arith.mul(refundInfo.getRefundAmount(), 100);
                wxPayRefundRequest.setRefundFee((int) refundFee);
                // 退款编码
                wxPayRefundRequest.setOutRefundNo(refundInfo.getRefundSn());


                // notifyUrl（通知结果）
//                wxPayRefundRequest.setNotifyUrl(domain.getMultishopDomainName() + refundInfo.getNotifyUrl());
                WxPayService wxPayService = wxConfig.getWxPayService(payType);
                wxPayRefundRequest.setNotifyUrl(properties.getProperty("wechat.wxNotifyUrl3"));
                wxPayService.refund(wxPayRefundRequest);

            } else if (isAliPay(payType)) {
                AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
//                request.setNotifyUrl(domain.getMultishopDomainName() + refundInfo.getNotifyUrl());
                request.setNotifyUrl(properties.getProperty("wechat.wxNotifyUrl3"));
                AlipayTradeRefundModel alipayTradeRefundModel = new AlipayTradeRefundModel();
                alipayTradeRefundModel.setOutTradeNo(refundInfo.getPayNo());
                alipayTradeRefundModel.setRefundAmount(Double.toString(refundInfo.getRefundAmount()));
                alipayTradeRefundModel.setOutRequestNo(refundInfo.getRefundSn());
                request.setBizModel(alipayTradeRefundModel);
                AlipayTradeRefundResponse response = aliPayConfig.getAlipayClient().certificateExecute(request);
                // 对fund_change必须了解为本次退款操作，若相同退款参数调用，第一次返回fund_change=Y，第二次返回fund_change=N，但是第二次仍返回退款信息。
                if (response.isSuccess() && StrUtil.isNotBlank(response.getFundChange())) {
                    return true;
                }
            } else if (Objects.equals(payType, PayType.BALANCE)) {
                applicationContext.publishEvent(new BalanceRefundEvent(refundInfo));
            } else if (Objects.equals(payType, PayType.PAYPAL)) {
                // payType申请退款
                return payPalConfig.refundOrder(refundInfo);
            }
        } catch (WxPayException e) {
            e.printStackTrace();
            throw new YamiShopBindException(e.getCustomErrorMsg(), e);
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new YamiShopBindException(e.getErrMsg(), e);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public Boolean cancelOrderQrCode(List<OrderSettlement> orderSettlements) throws WxPayException, AlipayApiException {
        Map<Integer, String> payTypeMap = orderSettlements.stream().collect(Collectors.toMap(OrderSettlement::getPayType, OrderSettlement::getPayNo));
        for (Integer payType : payTypeMap.keySet()) {
            PayType type = PayType.instance(payType);
            if (isWxPay(type)) {
                WxPayService wxPayService = wxConfig.getWxPayService(type);
                wxPayService.closeOrder(payTypeMap.get(payType));
                return true;
            } else if (isAliPay(type)) {
                AlipayTradeCloseRequest tradeCloseRequest = new AlipayTradeCloseRequest();
                AlipayTradeCloseModel tradeCloseModel = new AlipayTradeCloseModel();
                tradeCloseModel.setOutTradeNo(payTypeMap.get(payType));
                tradeCloseRequest.setBizModel(tradeCloseModel);
                AlipayTradeCloseResponse response = aliPayConfig.getAlipayClient().execute(tradeCloseRequest);
                logger.warn(response.getBody());
                if (response.isSuccess()) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public RefundInfoBo validateAndGetRefundInfo(HttpServletRequest request, PayType payType, String xmlData) throws UnsupportedEncodingException, WxPayException, AlipayApiException {

        RefundInfoBo refundInfoBo = new RefundInfoBo();
        refundInfoBo.setIsRefundSuccess(false);

        if (isWxPay(payType)) {
            WxPayRefundNotifyResult wxPayRefundNotifyResult = wxConfig.getWxPayService(payType).parseRefundNotifyResult(xmlData);
            WxPayRefundNotifyResult.ReqInfo reqInfo = wxPayRefundNotifyResult.getReqInfo();
            //
            if ("SUCCESS".equals(reqInfo.getRefundStatus())) {
                refundInfoBo.setIsRefundSuccess(true);
                refundInfoBo.setSuccessString(WX_SUCCESS_XML);
                refundInfoBo.setRefundNo(reqInfo.getOutRefundNo());
                refundInfoBo.setBizRefundNo(reqInfo.getRefundId());
            }
        } else if (isAliPay(payType)) {
            Map<String, String> params = parseAliNotifyAndGetResult(request);
            if (Objects.equals(params.get("trade_status"), "TRADE_SUCCESS") || Objects.equals(params.get("trade_status"), "TRADE_CLOSED")) {
                refundInfoBo.setIsRefundSuccess(true);
                refundInfoBo.setRefundNo(params.get("out_biz_no"));
                refundInfoBo.setBizRefundNo("");
                refundInfoBo.setSuccessString(ALI_SUCCESS);
            }
        }


        return refundInfoBo;
    }

    /**
     * 获取支付宝POST过来反馈信息
     *
     * @param request
     * @return
     * @throws UnsupportedEncodingException
     * @throws AlipayApiException
     */
    private Map<String, String> parseAliNotifyAndGetResult(HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException {
//        Map<String, String> params = getRequestParamsMap(request);
//        //调用SDK验证签名
//        boolean signVerified = AlipaySignature.rsaCertCheckV1(params, shopConfig.getAlipay().getAlipayCertPath(), cn.hutool.core.util.CharsetUtil.UTF_8, AlipayConstants.SIGN_TYPE_RSA2);
//
//        if (!signVerified) {
//            // 验签失败
//            throw new YamiShopBindException("yami.sign.check");
//        }
//        return params;

        //乱码解决，这段代码在出现乱码时使用
        request.setCharacterEncoding("utf-8");
        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<>(16);
        Map<String, String[]> requestParams = request.getParameterMap();
        for (String name : requestParams.keySet()) {
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }
        String aliPublicKey = "";
        try {
            Properties properties = PropertiesLoaderUtils.loadAllProperties("config/alipayAuth.properties");
            aliPublicKey = properties.getProperty("alipay.aliPublicKey");
        } catch (Exception e) {
            e.printStackTrace();
        }

        //调用SDK验证签名
//        boolean signVerified = AlipaySignature.rsaCertCheckV1(params, aliPublicKey, cn.hutool.core.util.CharsetUtil.UTF_8, AlipayConstants.SIGN_TYPE_RSA2);
        boolean signVerified = AlipaySignature.rsaCheckV1(params, aliPublicKey, cn.hutool.core.util.CharsetUtil.UTF_8, AlipayConstants.SIGN_TYPE_RSA2);
        if (!signVerified) {
            throw new YamiShopBindException("yami.sign.check");
        }
        return params;

    }

    /**
     * 获取请求参数
     */
    private Map<String, String> getRequestParamsMap(HttpServletRequest request) throws UnsupportedEncodingException {
        //乱码解决，这段代码在出现乱码时使用
        request.setCharacterEncoding("utf-8");
        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<>(16);
        Map<String, String[]> requestParams = request.getParameterMap();
        for (String name : requestParams.keySet()) {
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }
        return params;
    }


    @Override
    public PayInfoBo paypalCapture(String token) {
        // 执行扣款
        PayInfoBo payInfoBo = payPalConfig.captureOrder(token);
        // 查询扣款信息, 需要用到第三方交易号
//        payPalConfig.getCapture(payInfoBo.getBizPayNo());
        return payInfoBo;
    }

    private boolean isWxPay(PayType payType) {
        return (Objects.equals(payType, PayType.WECHATPAY)
                || Objects.equals(payType, PayType.WECHATPAY_SWEEP_CODE)
                || Objects.equals(payType, PayType.WECHATPAY_H5)
                || Objects.equals(payType, PayType.WECHATPAY_MP)
                || Objects.equals(payType, PayType.WECHATPAY_APP));
    }

    private boolean isAliPay(PayType payType) {
        return (Objects.equals(payType, PayType.ALIPAY_H5)
                || Objects.equals(payType, PayType.ALIPAY)
                || Objects.equals(payType, PayType.ALIPAY_APP));
    }
}
